package com.loong.socket.net;


import android.os.Message;
import android.util.Log;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Locale;

import com.loong.socket.handler.SocketUiHandler;
import com.loong.socket.model.TzcFileInternet;
import com.loong.socket.utils.FileUtils;

import static com.loong.socket.consts.Consts.*;


public class TcpClient extends Thread {
    private static final String TAG = TcpClient.class.getSimpleName();
    private static final String DEFAULT_DOMAIN = "10.1.23.200";
    private static final int DEFAULT_PORT = 222;
    private static final int DEFAULT_SOCKET_CONNECT_TIMEOUT = 5 * 1000;
    private static final int DEFAULT_SOCKET_RW_TIMEOUT = 30;
    private static final byte[] DEFAULT_DATA_TO_SEND = "data from client".getBytes();
    private String domain = DEFAULT_DOMAIN;
    private int port = DEFAULT_PORT;
    private byte[] mDataToSend = DEFAULT_DATA_TO_SEND;

    public static int mdensityDpi = 720;//默认720分辨率吧

    private SocketUiHandler mUiHandler;

    private String mFilePath;

    public TcpClient(String filePath, String domain, int port, SocketUiHandler uiHandler) {
        this.mFilePath = filePath;
        this.mUiHandler = uiHandler;
        this.domain = domain;
        this.port = port;
    }

    public void setDataToSend(byte[] aDataToSend) {
        this.mDataToSend = aDataToSend;
    }

    @Override
    public void run() {
        super.run();
        sendTest();
    }

    private void updateUiHandler(int msg) {
        if (mUiHandler != null) {
            mUiHandler.sendEmptyMessage(msg);
        }
    }

    private void updateUiHandler(int msg, String content) {
        if (mUiHandler != null) {
            Message message = Message.obtain();
            message.what = msg;
            message.obj = content;
            mUiHandler.sendMessage(message);
        }
    }

    boolean m_fwrit_exit_flag;
    int m_fwrit_pos;
    int m_fwrit_sum;
    int m_length;
    int m_sended;
    short m_snd_size;
    boolean m_need_ack;
    byte[] m_snd_buffer;
    byte[] m_rev_buffer;
    private final int _SelfHeadSize = 50;  //信息头长度
    private final int _BLOCKSIZE = 1024; //信息数据长度

    private final short _DATAFILETYPE = 0;   //文件类型（表示是DST或DSB文件发送，电控成功接收返回_ACTRECIBLOCK,失败返回MEMORYFULLFAIL，_FILETYPEERROR,_BUSYSOCANC）
    private final short _ORDERLINKONLY = 2;   //仅仅是联路连通检测（PC发给电控,查询联路连接是否连接成功。电控返回_ORDERLINKACT）
    private final short _ORDERLINKACT = 3;   //连路连通检测回答（电控发给PC）
    private final short _ACTRECIBLOCK = 4;   //接收到包回应，数据包把接收到编号，包号附带上（表示接收到文件数据成功，返回包头）
    private final short _CANCELDATE = 5;   //取消数据或文件传输，编号带上(PC发给电控中途取消通信)
    private final short _MEMORYFULLFAIL = 6;   //内存满失败
    private final short _FILETYPEERROR = 7;   //错误的文件类型
    private final short _BUSYSOCANC = 10;  //忙，所以数据取消（表示下面设备繁忙。文件无法接收）。
    private final short _GETBMPXYSIZE = 21;  //获取下面图片显示的范围区域,供手写软件缩放jpg图片大小用
    private final short _ACKBMPXYSIZE = 22;  //服务器在接收到客户端_GETBMPXYSIZE命令后回复_ACKBMPXYSIZE，回复格式请参考下面说明


    private void sendTest() {
        boolean goOn = true;
        boolean IsDst = false;
        // 设置超时间
        int _sec = DEFAULT_SOCKET_RW_TIMEOUT;
        System.out.println("----------Client----------");
        System.out.println("----------DPI----------" + mdensityDpi);
        Socket socket = null;
        try {
            // 与服务端建立连接
            Log.i(TAG, "connect begin: domain = " + domain + ", port = " + port);
            updateUiHandler(MSG_CONNECT_BEGIN);
            socket = new Socket();
            socket.connect(new InetSocketAddress(domain, port), DEFAULT_SOCKET_CONNECT_TIMEOUT);
            socket.setSoTimeout(_sec * 1000);

            System.out.println("connect to server success\n");
            updateUiHandler(MSG_CONNECT_SUCCESS);

            // 往服务端写数据
            updateUiHandler(MSG_SEND_BEGIN);
//            Writer writer = new OutputStreamWriter(socket.getOutputStream());
            DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
            DataInputStream dis = new DataInputStream(socket.getInputStream());
            System.out.println("start to write\n");
            System.out.println(Arrays.toString(mDataToSend));

            boolean start_flag = false;//传输使能标识
            int times = 0; //协议包号
            byte[] buffer = null;

            m_fwrit_exit_flag = false;
            m_fwrit_pos = 0;
            m_fwrit_sum = 0;
            m_length = 0;
            m_sended = 0;
            m_snd_size = 0;
            m_need_ack = true;
            m_snd_buffer = new byte[_SelfHeadSize + _BLOCKSIZE];
            m_rev_buffer = new byte[_SelfHeadSize];


            // get dimension of the bitmap from server
            // send request
            m_snd_size = _SelfHeadSize;
            TzcFileInternet.msgTypeInfo(m_snd_buffer, _GETBMPXYSIZE, times, (byte) 0, "");
            dos.write(m_snd_buffer, 0, m_snd_size);//dos.write向服务器发送数据

            // get ack and retrieve width and height info
            // read ack
            if (true) {
                int ret = 0;
                int loop = 0;
                do {
                    Arrays.fill(m_rev_buffer, (byte) 0);
                    ret = dis.read(m_rev_buffer, 0, _SelfHeadSize);//读取服务器发来的数据
                    Log.i(TAG, "get bitmap width and height receive ret = " + ret);
                    if (ret == _SelfHeadSize) break;
                    Thread.sleep(10);
                } while (loop++ < 100);

                if (ret != _SelfHeadSize) { // server ack receive error
                    updateUiHandler(MSG_ACK_ERROR);
                    goOn = false;
                }
            }

            if (goOn) {
                do {

                    //发送文件吧
                    File file = new File(mFilePath); // 创建File对象//"/sdcard/003Z.DST"
                    if (file.exists()) { // 返回文件存在状态
                        this.setDataToSend(FileUtils.fileToBytes(mFilePath));//"/sdcard/003Z.DST"
                        IsDst = true;
                    } else {
                        IsDst = false;
                    }


                } while (false);
            }

            if (goOn) {

                // 获取当前时间的实例
                Calendar calendar = Calendar.getInstance();
                // 设置日期时间格式   "yyyy-MM-dd HH:mm:ss", Locale.getDefault()
                SimpleDateFormat dateFormat = new SimpleDateFormat("ddHHmmss", Locale.getDefault());
                // 格式化当前时间
                String formattedDateTime = dateFormat.format(calendar.getTime());
                String name;
                if (IsDst) {
                    name = formattedDateTime + "DST";//"00000000DST";//文件名最多11个字节 日时分秒
                } else {
                    name = formattedDateTime + "TZC";//"00000000TZC";//文件名最多11个字节 日时分秒
                }


                while (!m_fwrit_exit_flag)// && m_fwrit_pos<m_fwrit_sum)
                {
                    if (!start_flag) { // 打开文件
                        m_length = 0;
                        m_sended = 0;
                        times = 1;
                        buffer = null;
                        if (mDataToSend != null) {
                            buffer = mDataToSend;
                            m_length = mDataToSend.length;
                            start_flag = true;
                        } else {
                            m_fwrit_pos++;
                        }
                    }
                    if (start_flag/* && m_snd_size <= 0*/) {//传输文件
                        if (m_sended < m_length) {
                            Arrays.fill(m_snd_buffer, (byte) 0);
                            if ((m_length - m_sended) <= _BLOCKSIZE) {//最后一个包
                                m_snd_size = (short) (m_length - m_sended);
                                TzcFileInternet.msgTypeInfo(m_snd_buffer, _DATAFILETYPE, times, (byte) 1, "");
                            } else {//前面的包
                                m_snd_size = _BLOCKSIZE;
                                TzcFileInternet.msgTypeInfo(m_snd_buffer, _DATAFILETYPE, times, (byte) 0, "");

                            }
                            TzcFileInternet.msgFileInfo(m_snd_buffer, m_length, name, m_snd_size);
                            System.arraycopy(buffer, m_sended, m_snd_buffer, _SelfHeadSize, m_snd_size);
                            m_sended += m_snd_size;
                            m_snd_size += _SelfHeadSize;

                            Log.i(TAG, "send times = " + times);

                            dos.write(m_snd_buffer, 0, m_snd_size);
//                        dos.flush();

                            // read ack
                            if (m_need_ack) {
                                int ret = 0;
                                int loop = 0;
                                do {
                                    Arrays.fill(m_rev_buffer, (byte) 0);
                                    ret = dis.read(m_rev_buffer, 0, _SelfHeadSize);
                                    Log.i(TAG, "receive ret = " + ret);
                                    if (ret == _SelfHeadSize) break;
                                    Thread.sleep(10);
                                } while (loop++ < 100);

                                if (ret != _SelfHeadSize) { // server ack receive error
                                    updateUiHandler(MSG_ACK_ERROR);
                                    break;
                                }
                            }

                            int protocolType = TzcFileInternet.getProtocolType(m_rev_buffer);
                            Log.i(TAG, "protoType = " + protocolType);

                            goOn = false;
                            switch (protocolType) {
                                case _ACTRECIBLOCK:
                                    int blockNo = TzcFileInternet.getBlockNo(m_rev_buffer);
                                    Log.i(TAG, "blockNo = " + blockNo);
                                    if (blockNo == times) {
                                        goOn = true;
                                    }
                                    goOn = true;
                                    break;
                                default:    // others, error
                                    updateUiHandler(MSG_ACK_ERROR);
                                    break;
                            }

                            if (!goOn) break;   // ack error, jump out
                            times++;

                            if (m_sended >= m_length) {
                                m_fwrit_pos++;
                                start_flag = false;
                                buffer = null;
                                m_fwrit_exit_flag = true;
                                m_snd_buffer = null;
                            }
                        }
                    }

                }
                m_fwrit_pos = m_fwrit_sum = 0;
                updateUiHandler(MSG_SEND_FINISH);
                System.out.println("write finish\n");
                updateUiHandler(MSG_CONNECT_CLOSE);
            }

            Thread.sleep(100);  // delay to close socket

            // close
//            writer.close();
            dos.close();
            socket.close();


        } catch (SocketTimeoutException e) {
            e.printStackTrace();
            updateUiHandler(MSG_CONNECT_FAIL, "SocketTimeoutException-连接超时");
        } catch (SocketException e) {
            e.printStackTrace();
            updateUiHandler(MSG_CONNECT_FAIL, "SocketException" + e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
            updateUiHandler(MSG_CONNECT_FAIL, "Exception" + e.getMessage());
        }

    }
}
